/** This class makes all xml-parsing jobs 
 *  @author ChrM */

package info.chrm.jtalker;

import java.awt.*;
import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
import java.util.*;

public class JTalkerParser extends DefaultHandler {
   // Static Vars
   public static final String USER_TYPE_WEBCLIENT = "webclient";
   public static final String USER_TYPE_PERSISTENT = "persistent";
   public static final String USER_TYPE_CHATROOM = "chatroom";
   
   /** MEMBER tag includes some members informations.
       This tag provides follows properties: NAME [, STATUS, PASSWD] */
   public static final String MEMBER = "MEMBER";
   public static final String MEMBER_NAME = "name";
   public static final String MEMBER_STATUS = "status";
   public static final String MEMBER_PASSWD = "passwd";
   public static final String MEMBER_IP = "ip";
   public static final String MEMBER_TYPE = "type";
   public static final String MEMBER_SID = "sid";

   
   /** MESSAGE tag includes some messages.
       This tag provides follows properties: NAME, CONTENT, SENDER, STATUS, TO, TYPE */
   public static final String MESSAGE = "MESSAGE";
   public static final String MESSAGE_CONTENT = "content";
   public static final String MESSAGE_SENDER = "sender";
   public static final String MESSAGE_STATUS = "status";
   public static final String MESSAGE_TO = "to";
   public static final String MESSAGE_TYPE = "type";
   public static final String MESSAGE_SUBJECT = "subject";
   public static final String MESSAGE_ID = "id";

   /** Old style server messages: Error or Info */   
   public static final String ERROR = "ERROR";
   public static final String INFO = "INFO";
   public static final int ANSWER_INFO = 0;
   public static final int ANSWER_ERROR = 1;
   public static final int ANSWER_UPDATE = 2;

   /** Config File Tags */
   public static final String CONFIG_ROOT = "JTALKER-CLIENT";
   public static final String CONFIG_ACCOUNT = "ACCOUNT";
   public static final String CONFIG_ACCOUNT_NAME = "name";
   public static final String CONFIG_ACCOUNT_PASSWD = "passwd";
   public static final String CONFIG_SERVER = "SERVER";
   public static final String CONFIG_SERVER_IP = "ip";
   public static final String CONFIG_SERVER_PORT = "port";
   public static final String CONFIG_SERVER_SSL = "ssl";
   public static final String CONFIG_LOOK = "LOOK";
   public static final String CONFIG_LOOK_PLAF = "plaf";
   public static final String CONFIG_LOOK_TEXTCOLOR = "textcolor";
   public static final String CONFIG_LOOK_BGCOLOR = "bgcolor";
   public static final String CONFIG_APPS = "APPS";
   public static final String CONFIG_APPS_BROWSER = "browser";
   public static final String CONFIG_APPS_MAIL = "mail";
   public static final String CONFIG_NOTIFICATION = "NOTIFICATION";
   public static final String CONFIG_NOTIFICATION_USERLIST = "user_list";
   public static final String CONFIG_PLUGINS = "PLUGINS";
   public static final String CONFIG_PLUGINS_DIR = "plugin_dir";
   public static final String CONFIG_PLUGINS_SHORTCUT = "plugin_shortcuts";
   
   
   
   /** Constructor */
   public JTalkerParser() {
   }

   /** Starts parsing */
   public boolean parsing(String _xmlStream) {
      // reset
      this.listMembers.clear();
      this.listMessages.clear();
      this.lastAnswer = null; // Last recieved server answer
      this.lastAnswerType = JTalkerParser.ANSWER_INFO; // Last recieved server answer
      this.lastCommand = null; // Last recieved command
      this.lastCommandResponse = null; // Last recieved command-response
      
      try { 
	 SAXParserFactory fct = SAXParserFactory.newInstance();
	 fct.setValidating(false);
	 SAXParser saxParser = fct.newSAXParser();
	 InputStream is = new ByteArrayInputStream(_xmlStream.getBytes("UTF-8"));
	 saxParser.parse(is, this);
      } catch (Exception e) { 
	 //e.printStackTrace();
	 return false;
      } 

      return true;
   }

   /** Starts parsing of config file */
   public boolean parsing(File _configFile) {
      // reset
      this.lastError = null;
      this.parsingConfigFile = false;
      this.client_settings = new ClientSettings();

      if(!_configFile.canRead()) {
	 this.lastError = "Can't load file";
	 return false;
      }
      
      try { 
	 SAXParserFactory fct = SAXParserFactory.newInstance();
	 fct.setValidating(false);
	 SAXParser saxParser = fct.newSAXParser();
	 saxParser.parse(_configFile, this);
      } catch (Exception e) { 
	 //e.printStackTrace();
	 return false;
      } 

      // Maybe old file format?
      return this.parsingConfigFile;
   }

   /** Gets client settings */
   public ClientSettings getClientSettings() {
      return this.client_settings;
   }

   //---------------------------------------------------------------
   /** Gets the message */
   public String getMessage() {
      //return this.messageContent;
      if(this.getAMessage(0) != null)
	 return this.getAMessage(0).content;
      else return null;
   }

   /** Gets the first Sender (on server site) */
   public String getSender() {
      //return this.messageSender;
      if(this.getAMessage(0) != null)
	 return this.getAMessage(0).sender;
      else return null;
   }

   /** Gets the first sender status (on server site; in the message Tag)*/
   public String getStatus() {
      //return this.messageSenderStatus;
      if(this.getAMessage(0) != null)
	 return this.getAMessage(0).senderStatus;
      else return "normal";
   }

   /** Gets the first sender status (in the member Tag).
    *  Should be used only for update the own status! */
   public String getMemberStatus() {
      if(this.listMembers.size() == 1)
	 return ((OneMember)this.listMembers.get(0)).status;
      else 
	 return null;
   }

   /** Gets a list of Receivers for the first message (separated by double semikolon: ';;')*/
   public String getRecs() {
      //return this.messageReceivers;
      if(this.getAMessage(0) != null)
	 return this.getAMessage(0).messageReceivers;
      else return null;
   }

   /** Gets the recieved command */
   public String getCommand() {
      return this.lastCommand;
   }
   
   /** Gets the recieved command-response */
   public String getCommandResponse() {
      return this.lastCommandResponse;
   }

   /** Gets a member by number */
   public OneMember getAMember(int _index) {
      try { 
	 return (OneMember)this.listMembers.get(_index);
      } catch (Exception e) { 
	 return null;
      } 
   }

   /** Gets a message by number */
   public OneMessage getAMessage(int _index) {
      try { 
	 return (OneMessage)this.listMessages.get(_index);
      } catch (Exception e) { 
	 return null;
      } 
   }

   /** Gets number of members */
   public int getMemberNum() {
      return this.listMembers.size();
   } 

   /** Gets number of messages */
   public int getMessagesNum() {
      return this.listMessages.size();
   } 

   /** Gets the last Server answer (could be an error or an information) */
   public String getAnswer() {
      return this.lastAnswer;
   }
   
   /** Gets the last Servers answer type */
   public int getAnswerType() {
      return this.lastAnswerType;
   }

   /** Splits Recs to an ArrayList and get it */
   public String [] getRecsAsArray(int _index) {
      return this.getAMessage(_index).messageReceivers.split(";;");      
   }

   /** Splits Recs to an ArrayList and get it (for the first message) */
   public String [] getRecsAsArray() {
      return this.getAMessage(0).messageReceivers.split(";;");      
   }
   //---------------------------------------------------------------
 
   /** SAX-methods */
   public void startElement(String _nsURI, String _sName, String _qName, Attributes _attrs) {
      this.currentTag = _qName;
      
      if(this.currentTag.equalsIgnoreCase(MESSAGE)) {
	 // check message type
	 String tmpType = _attrs.getValue(MESSAGE_TYPE);
	 if(tmpType != null && tmpType.equalsIgnoreCase("error")) {
	    this.lastAnswer = _attrs.getValue(MESSAGE_CONTENT);
	    this.lastAnswerType = JTalkerParser.ANSWER_ERROR;
	 } else if(tmpType != null && tmpType.equalsIgnoreCase("info")) {
	    this.lastAnswer = _attrs.getValue(MESSAGE_CONTENT);
	    this.lastAnswerType = JTalkerParser.ANSWER_INFO;
	 } else if(tmpType != null && tmpType.equalsIgnoreCase("update")) {
	    this.lastAnswer = _attrs.getValue(MESSAGE_CONTENT);
	    this.lastAnswerType = JTalkerParser.ANSWER_UPDATE;
	 //} else if(tmpType != null && tmpType.equalsIgnoreCase("command")) {
	 //   this.lastCommand = _attrs.getValue(MESSAGE_CONTENT);
	 //} else if(tmpType != null && tmpType.equalsIgnoreCase("command_response")) {
	 //   this.lastCommandResponse = _attrs.getValue(MESSAGE_CONTENT);
	 } else	 
	    this.listMessages.add( new OneMessage(
			        _attrs.getValue(MESSAGE_CONTENT),
				_attrs.getValue(MESSAGE_SENDER),
				_attrs.getValue(MESSAGE_STATUS),
				_attrs.getValue(MESSAGE_TO),
				tmpType,
				_attrs.getValue(MESSAGE_SUBJECT),
                                _attrs.getValue(MESSAGE_ID)) );
	 
      } else if(this.currentTag.equalsIgnoreCase(MEMBER)) {
	 // A Member list is received
	 this.listMembers.add( new OneMember(
			      _attrs.getValue(MEMBER_NAME),
			      _attrs.getValue(MEMBER_STATUS),
			      _attrs.getValue(MEMBER_PASSWD),
			      _attrs.getValue(MEMBER_IP),
			      _attrs.getValue(MEMBER_TYPE),
			      _attrs.getValue(MEMBER_SID)) );
      } else if(this.currentTag.equalsIgnoreCase(ERROR)) {
	 // OBSOLETE: Server sends an error message
	 this.lastAnswer = _attrs.getValue("message");
	 this.lastAnswerType = JTalkerParser.ANSWER_ERROR;
      } else if(this.currentTag.equalsIgnoreCase(INFO)) {
	 // OBSOLETE: Server sends an info message
	 this.lastAnswer = _attrs.getValue("message");
	 this.lastAnswerType = JTalkerParser.ANSWER_INFO;
      } else if(this.currentTag.equalsIgnoreCase(CONFIG_ROOT)) {
	 // We are parsing the config file
	 this.parsingConfigFile = true;
      } else if(this.currentTag.equalsIgnoreCase(CONFIG_ACCOUNT) &&  this.parsingConfigFile) {
	 this.client_settings.account_name = _attrs.getValue(CONFIG_ACCOUNT_NAME);
	 this.client_settings.account_passwd = _attrs.getValue(CONFIG_ACCOUNT_PASSWD);
      } else if(this.currentTag.equalsIgnoreCase(CONFIG_SERVER) &&  this.parsingConfigFile) {
	 this.client_settings.server_ip = _attrs.getValue(CONFIG_SERVER_IP);
	 this.client_settings.server_port = _attrs.getValue(CONFIG_SERVER_PORT);
	 this.client_settings.server_ssl = _attrs.getValue(CONFIG_SERVER_SSL);
      } else if(this.currentTag.equalsIgnoreCase(CONFIG_LOOK) &&  this.parsingConfigFile) {
	 this.client_settings.look_plaf = _attrs.getValue(CONFIG_LOOK_PLAF);
	 this.client_settings.look_textcolor = _attrs.getValue(CONFIG_LOOK_TEXTCOLOR);
	 this.client_settings.look_bgcolor = _attrs.getValue(CONFIG_LOOK_BGCOLOR);
      } else if(this.currentTag.equalsIgnoreCase(CONFIG_APPS) &&  this.parsingConfigFile) {
	 this.client_settings.apps_browser = _attrs.getValue(CONFIG_APPS_BROWSER);
	 this.client_settings.apps_mail = _attrs.getValue(CONFIG_APPS_MAIL);
      } else if(this.currentTag.equalsIgnoreCase(CONFIG_NOTIFICATION) &&  this.parsingConfigFile) {
	 this.client_settings.notif_userlist = _attrs.getValue(CONFIG_NOTIFICATION_USERLIST);
      } else if(this.currentTag.equalsIgnoreCase(CONFIG_PLUGINS) &&  this.parsingConfigFile) {
	 this.client_settings.plugins_dir = _attrs.getValue(CONFIG_PLUGINS_DIR);
	 this.client_settings.plugins_shortcuts = _attrs.getValue(CONFIG_PLUGINS_SHORTCUT);
      }
   }

   /** SAX-methods */
   public void characters(char[] ch, int start, int length) {
   }

   /** SAX-methods */
   public void endElement(String _uri, String _localName, String _qName) {
   }

   /** SAX-error-methods */
   public void error(SAXParseException _e) {
      System.out.println("*** SAX-Parser Error: " + _e.toString());
   }

   /** SAX-error-methods */
   public void fatalError(SAXParseException _e) {
      System.out.println("*** SAX-Parser Error: " + _e.toString());
   }

   /** Gets last Error */
   public String getLastError() {
      return this.lastError;
   }

   // private members
   private String currentTag = null;   
   private String lastAnswer = null;
   private String lastCommand = null;
   private String lastCommandResponse = null;
   private String lastError = null;
   private boolean parsingConfigFile = false;
   private ClientSettings client_settings = null;
   private int lastAnswerType = JTalkerParser.ANSWER_INFO;
   //private String messageContent = null;
   //private String messageSender = null;
   //private String messageSenderStatus = null;
   //private String messageReceivers = null;
   private ArrayList listMembers = new ArrayList();
   private ArrayList listMessages = new ArrayList();

   /** This inner class represents a member */
   public class OneMember {
      public OneMember(String _name, String _status, String _passwd, String _ip, String _type, String _sid) {
	 this.name = _name;
	 this.status = _status;
	 this.passwd = _passwd;
	 this.ip = _ip;
	 if(_type != null)
	    this.type = _type;
	 else 
	    this.type = USER_TYPE_PERSISTENT;
	 this.sid = _sid;
      }
      
      public String name = null;    // like "UserA"
      public String status = null;  // "normal", "busy", "robot"
      public String passwd = null;  // "xxx"
      public String ip = null;	    // "127.0.0.1"
      public String type = USER_TYPE_PERSISTENT;
      public String sid = null;
   }

   /** This inner class represents a message */
   public class OneMessage {
      public OneMessage(String _content,
			String _sender,
			String _senderStatus,
			String _messageReceivers,
			String _messageType,
			String _messageSubject,
                        String _id) {

	 this.content = _content;
	 this.sender = _sender;
	 this.senderStatus = _senderStatus;
	 this.messageReceivers = _messageReceivers;
	 this.messageType = _messageType;
	 this.messageSubject = _messageSubject;
         this.id = _id;
      }

      public String content = null;	     // Text
      public String sender = null;	     // Member name
      public String senderStatus = "normal";     // "normal", "busy", "robot"
      public String messageReceivers = null; // "UserA;;UserB;;UserC"
      public String messageType = null;	     // "normal", "error", "info", "command"
      public String messageSubject = null;   // "picture.jpg"
      public String id = null;               // "abgh2123"
   }
    
   /** This inner class represents the client settings parsed from a config file */
   public class ClientSettings {
      public ClientSettings() {
      }

      public String account_name = System.getProperty("user.name");
      public String account_passwd = "";
      public String server_ip = "localhost";
      public String server_port = "4444";
      public String server_ssl = "false";
      public String look_plaf = "localhost";
      public String look_textcolor = "localhost";
      public String look_bgcolor = "localhost";
      public String apps_browser = "firefox";
      public String apps_mail = "kmail";
      public String notif_userlist = "2";
      public String plugins_dir = System.getProperty("user.home") + File.separator;
      public String plugins_shortcuts = "";
   }
}
